<?php
/**
 *============================================================================
 * Cornice PHP Framework & Website Builder
 * Freeware (f) 2011 by Inforo Software & Computer
 *============================================================================
 */
 
/**
 * Main class of Cornice core
 * @author Nicola Dinh
 * @version 2011.04.2216
 */
class system {

  /*
   * All properties
   */
  public static $devmode = false; 
  private static $module = null; //multi-level array (info only)
  private static $page = null; //multi-level array (info only)

  public static $skin = null; //current skin
  public static $lang = null; //current language
 
  private static $layout_obj = null;
  private static $page_obj = null;  
  public static $blocks = array(); //list of loaded blocks
  
  public static $title = null;
  public static $website = null;

  /**
   * Default instance level constructor
   * Just decorative, should not be called due to having only static methods
   */
  public function __construct() {
    //
  }
  
  /**
   * Get requested module information (from module xml)
   * @return array The array parsed from module xml
   */
  public static function get_module_info() {
    return self::$module;
  }

  /**
   * Get requested page information (from page xml)
   * @return array The array parsed from page xml
   */
  public static function get_page_info() {
    return self::$page;
  }
  
  /**
   * Load all PHP classes in a certain directory
   * @param string $dir_path The path to directory
   * @return boolean 'true' if all files loaded well, 'false' otherwise
   */
  private static function load_php_classes($dir_path) {
    if (file_exists("$dir_path/classes.txt")) {
      $classes = file("$dir_path/classes.txt");
      foreach ($classes as $class) {
        $class = trim($class);
        require_once("$dir_path/$class.php");
      }
      return;
    }
    
    //if 'classes.txt' not found
    $dir_handle = opendir($dir_path);
    if ($dir_handle===false)
      return false;
    
    //load files
    while (($file=readdir($dir_handle))!==false)
      if (is_file("$dir_path/$file"))
        require_once("$dir_path/$file");      
      
    //close dir  
    closedir($dir_handle);
    return true;
  }
  
  /**
   * Load all core classes
   */
  public static function load_core_classes() {
    self::load_php_classes("classes/core/bases");
    self::load_php_classes("classes/core/statics");
    self::load_php_classes("classes/core/dynamics");
  }
  
  /**
   * Load all custom classes
   */
  public static function load_custom_classes() {
    self::load_php_classes("classes/customs/bases");
    self::load_php_classes("classes/customs/statics");
    self::load_php_classes("classes/customs/dynamics");  
  }

  /**
   * Load module information (from 'modules/module.xml')
   */
  public static function load_module_info() {
    $module = request::get_module_name();
    $module_xml = "modules/$module.xml";
    
    //check file
    if (!file_exists($module_xml)) {
      response::error("'$module_xml' not found");
      exit;
    }
    
    //load xml
    self::$module = xml::arr_from_file($module_xml); 
  }
  
  /**
   * Load page information (from 'mcv/pages/views/xml/page.xml')
   */
  public static function load_page_info() {
    $module = request::get_module_name();
    $page = request::get_page_name(); 
    $page_xml = "mcv/pages/views/xml/$module"."_"."$page"."_page".".xml";
    
    //check page xml file
    if (!file_exists($page_xml)) {
      response::error("'$page_xml' not found");
      exit;
    }
    
    //load page xml
    self::$page = xml::arr_from_file($page_xml); 
  }

  /**
   * Create layout object
   */
  private static function create_layout_obj() {
    $layout = utils::get_arr_value(self::$page,"layout");

    //file paths    
    $mdl_path = "mcv/layouts/models/shared_".$layout."_layout_model.php";
    $cls_path = "mcv/layouts/controllers/shared_".$layout."_layout.php";
    $css_path = "mcv/layouts/views/css/shared_".$layout."_layout.css";
    $js_path = "mcv/layouts/views/js/shared_".$layout."_layout.js";
    $php_path = "mcv/layouts/views/php/shared_".$layout."_layout.php";
    
    //load classes
    if (!file_exists($mdl_path)) {
      response::error("'$mdl_path' not found");
      exit;
    }
    if (!file_exists($cls_path)) {
      response::error("'$cls_path' not found");
      exit;
    }
    require_once($mdl_path); //model
    require_once($cls_path); //controller

    //create instance of layout
    $layout_class = "shared_".$layout."_layout";
    $model_class = $layout_class."_model";
    $layout_obj = new $layout_class();
    $model_obj = new $model_class();
    
    //set properties
    $model_obj->class_path = $mdl_path;
    $layout_obj->class_path = $cls_path;    
    $layout_obj->css_path = $css_path;
    $layout_obj->js_path = $js_path;
    $layout_obj->php_path = $php_path;
    $layout_obj->model = $model_obj;

    self::$layout_obj = $layout_obj;    
  }
  
  /**
   * Create page object
   */
  private static function create_page_obj() {
    $module = request::get_module_name();
    $page = request::get_page_name();
    $skin = self::$skin; 

    //file paths    
    $mdl_path = "mcv/pages/models/$module"."_".$page."_page_model.php";
    $cls_path = "mcv/pages/controllers/$module"."_".$page."_page.php";
    $css_path = "mcv/pages/views/css/$module"."_".$page."_".$skin.".css";
    $js_path = "mcv/pages/views/js/$module"."_".$page."_page.js";
    $php_path = "mcv/pages/views/php/$module"."_".$page."_page.php";
    
    //block has css to follow skin, but if so such css file, use default
    //skin file of block (ie. $module_$block_block.css)
    if (!file_exists($css_path))    
      $css_path = "mcv/pages/views/css/$module"."_".$page."_page.css";
    
    //load classes
    if (!file_exists($mdl_path)) {
      response::error("'$mdl_path' not found");
      exit;
    }
    if (!file_exists($cls_path)) {
      response::error("'$cls_path' not found");
      exit;
    }    
    require_once($mdl_path); //model
    require_once($cls_path); //controller

    //create instance of layout
    $page_class = $module."_".$page."_page";
    $model_class = $page_class."_model";
    $page_obj = new $page_class();
    $model_obj = new $model_class();
    
    //set properties
    $model_obj->class_path = $mdl_path;
    $page_obj->class_path = $cls_path;
    $page_obj->css_path = $css_path;
    $page_obj->js_path = $js_path;
    $page_obj->php_path = $php_path;
    $page_obj->model = $model_obj;

    self::$page_obj = $page_obj;    
  }

  /**
   * Get instance of a block
   * @param string $block The name of block (eg. 'menu','header',...)
   */
  public static function get_block($block,$module=null) {
    if ($module===null)
      $module = request::get_module_name();
    $skin = self::$skin;  

    //file paths    
    $mdl_path = "mcv/blocks/models/$module"."_".$block."_block_model.php";
    $cls_path = "mcv/blocks/controllers/$module"."_".$block."_block.php";
    $css_path = "mcv/blocks/views/css/$module"."_".$block."_"."$skin.css";
    $js_path = "mcv/blocks/views/js/$module"."_".$block."_block.js";
    $php_path = "mcv/blocks/views/php/$module"."_".$block."_block.php";
    
    //block has css to follow skin, but if so such css file, use default
    //skin file of block (ie. $module_$block_block.css)
    if (!file_exists($css_path))
      $css_path = "mcv/blocks/views/css/$module"."_".$block."_block.css";
    
    //load classes
    if (!file_exists($mdl_path)) {
      response::error("'$mdl_path' not found");
      exit;
    }
    if (!file_exists($cls_path)) {
      response::error("'$cls_path' not found");
      exit;
    }    
    require_once($mdl_path); //model
    require_once($cls_path); //controller

    //create instance of layout
    $block_class = $module."_".$block."_block";
    $model_class = $block_class."_model";
    $block_obj = new $block_class();
    $model_obj = new $model_class();
    
    //set properties
    $model_obj->class_path = $mdl_path;
    $block_obj->class_path = $cls_path;
    $block_obj->css_path = $css_path;
    $block_obj->js_path = $js_path;
    $block_obj->php_path = $php_path;
    $block_obj->model = $model_obj;
    
    //set block ID
    if (!isset(self::$blocks[$block]))    
      self::$blocks[$block] = array();
    $count = count(self::$blocks[$block]);
    $block_obj->id = $block.$count;
    self::$blocks[$block][$count] = &$block_obj;

    return $block_obj;
  }
  
  /** 
   * Initialise all classes (mostly static ones)
   */
  public static function initialise_all() {
    if (file_exists("dump.txt"))
      unlink("dump.txt");
    
    config::initialise();
    request::initialise();
    session::initialise();
    phrase::initialise();
    
    self::load_module_info();
    self::load_page_info();  

    self::$website = config::get_website_name();
    self::$title = utils::get_arr_value(self::$page,"title");    
  }
  
  /**
   * Module authentication page should call this function when the 
   * requirements for accessing the module are all fulfilled.
   */
  public static function module_auth_passed() {
    $module_name = request::get_module_name();
    session::set_value($module_name."_auth","passed");
  }
  
  /**
   * Perform a sign-out action
   */
  public static function sign_out() {
    session::destroy();
  }
  
  /**
   * Check if current module is protected
   * @return boolean 'true' if protected, 'false' otherwise
   */
  private static function current_module_protected() {
    //no 'protected' tag in module.xml
    if (!isset(self::$module["protected"]))
      return true;
      
    //no value for 'protected' tag  
    if (!is_string(self::$module["protected"]))
      return true;
      
    //value of 'protected' tag isn't string "false"  
    if (strtolower(trim(self::$module["protected"]!=="false")))
      return true;
      
    return false;
  }
  
  /**
   * Get list of existing modules (xml files in 'modules' directory)
   * @return array The list of modules
   */
  public static function get_module_list() {
    $files = utils::get_files_in("modules","xml");
    if (count($files)==0)
      return array();
      
    //file names to module names  
    $modules = array();  
    foreach ($files as $file) {
      $parts = pathinfo($file);
      array_push($modules,$parts["filename"]);
    }
    
    return $modules;
  }

  /**
   * Get list of protected modules
   * @return array The list of protected modules
   */
  public static function get_protected_modules() {
    $module_names = self::get_module_list();
    $protecteds = array();
    
    //get module xml
    foreach ($module_names as $module_name) {
      $module_name = strtolower(trim($module_name));
      if ($module_name=="devtools")
        continue;
      
      $info = xml::arr_from_file("modules/$module_name.xml");
      if (!isset($info["protected"]))
        array_push($protecteds,$module_name);
      else 
      if (!is_string($info["protected"])) 
        array_push($protecteds,$module_name);
      else {
        $protected = strtolower(trim($info["protected"]));
        if ($protected!=="false")
          array_push($protecteds,$module_name);
      }
    }//foreach
    
    return $protecteds;
  }
  
  /**
   * Break all output buffers (those started by ob_start)
   */
  public static function break_all_buffers() {
    while (ob_get_level()>0)
      ob_end_clean();
  }
  
  /**
   * Check if the access to certain module is already granted
   * @return boolean 'true' is already granted, 'false' otherwise
   */
  private static function module_access_granted() {
    $module_name = request::get_module_name();
    $auth_status = session::get_value($module_name."_auth");
    if (!is_string($auth_status))    
      return false;

    //return
    return $auth_status==="passed";    
  }
  
  /**
   * Check module protection (module authentication)
   */
  private static function check_module_protection() { 
    $module_name = strtolower(trim(request::get_module_name())); 
    $page_name = strtolower(trim(request::get_page_name()));
    
    //no protection for 'devtools' coz accessible only from localhost
    if ($module_name=="devtools")
      return; 
     
    //redirect to authentication page if module is protected     
    if (self::current_module_protected()) 
    if (!self::module_access_granted())
    if ($page_name!=="modauth")
      response::redirect_to("$module_name"."_modauth");
  }
  
  /**
   * Check module accessibility
   */
  public static function check_security() { 
    $module_name = strtolower(trim(request::get_module_name()));
    
    //get module status
    $module_is_online = strtolower(trim(self::$module["online"])); 
    if ($module_is_online!=="true") {
      response::error("Module '$module_name' is offline");
      exit; 
    }
    
    //module 'devtools' accessible only from localhost
    if ($module_name=="devtools" && !self::is_localhost()) {
      response::error("Network access to 'devtools' denied");
      exit;
    }
      
    //check accessibility from internet
    $internet = self::$module["accessible"]["from"]["internet"];
    $internet = strtolower(trim($internet));
    if ($internet!=="true") 
      if (!self::is_localhost()) {
        response::error("Network access to '$module_name' denied");
        exit;
      }
      
    self::check_module_protection();
  }
  
  /**
   * Check user's preferences (skin & language)
   */
  public static function check_preferences() {
    $setlang = request::get_url_var("setlang");
    $setskin = request::get_url_var("setskin");
    
    //new lang preference?
    if ($setlang!=null)
      cookie::set("lang",$setlang);
  
    //new skin preference?
    if ($setskin!=null)
      cookie::set("skin",$setskin);
  
    //check skin in cookie jar
    $skin = cookie::get("skin");
    if ($skin==null || strlen($skin)==0)
      self::$skin = config::get_default_skin();
    else 
      self::$skin = $skin;
      
    //check language in cookie jar  
    $lang = cookie::get("lang");
    if ($lang==null || strlen($lang)==0)    
      self::$lang = config::get_default_lang();
    else
      self::$lang = $lang;
  }
  
  /**
   * Get created layout object
   */
  public static function layout() {
    return self::$layout_obj;
  }
  
  /**
   * Get created page object  
   */
  public static function page() {
    return self::$page_obj;
  }
  
  /**
   * Check if script called from localhost, meaning whether the web browser
   * running on server or on a network computer   
   * @return boolean 'true' if called from localhost
   */
  public static function is_localhost() {
    $remote = strtolower($_SERVER["REMOTE_ADDR"]); 
    
    //IP v4 or IP v6 loopbacks
    return $remote=="127.0.0.1" || $remote=="::1";
  }
  
  /**
   * Render 'head' tag and information inside
   */
  private static function render_head_tag() {
    $skin = self::$skin;
    $file_path = "skins/$skin/global.php";
    require_once($file_path);
  }
  
  /**
   * Render the whole layout (which contains page)
   */
  private static function render_layout() {
    //render
    self::$layout_obj->render_html();
  }

  /**
   * Render the requested page
   */
  public static function render_page() {
    //render
    self::$page_obj->render_html();
  }
  
  /**
   * Render the whole web page
   */
  public static function render_webpage() {
    self::create_layout_obj();
    self::create_page_obj();  
  
    ob_start(); 
    self::render_head_tag();
    self::render_layout();
    echo ob_get_clean();
  }
}